﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using DefPack;
using System.Runtime.InteropServices.ComTypes;
using DefPack.Entity;
using SaveSation.Utils;
using System.Text.Encodings.Web;
using System.Text.Unicode;
using DefPack.Common;
using System.Text.Json.Serialization;
using System.Configuration;
using System.IO.Compression;
using DefPack.Utils;
using SaveSation.Common;

namespace SaveSation.Entity
{
    public class ArchiveEntity : IComparable<ArchiveEntity>
    {
        public string GroupID { get; set; } = String.Empty;
        public string UUID { get; set; } = Guid.NewGuid().ToString().ToUpper().Replace("-", "");
        internal string FullPath => Path.Combine(StartForm.m_AppSetting.BackupPath, UUID);
        public string Name { get; set; } = String.Empty;
        public string Application { get; set; } = String.Empty;
        public string ArchivePath { get; set; } = String.Empty;
        public string SteamID { get; set; } = String.Empty;
        public byte Tick { get; set; } = 0;
        public short MaxCount { get; set; } = 0;
        public byte TriggerEvent { get; set; } = 0;
        public byte BackupContent { get; set; } = 0;
        public string Icon { get; set; } = String.Empty;
        public int SortNumber { get; set; } = 99;
        /// <summary>
        /// 还原动作
        /// </summary>
        public byte RestoreOperation { get; set; } = 0;
        /// <summary>
        /// 不检测项目
        /// </summary>
        public string UnDetect { get; set; } = String.Empty;
        /// <summary>
        /// 不备份项目
        /// </summary>
        public string UnBuckup { get; set; } = String.Empty;
        [NonSerialized]
        internal List<ArchiveItem> ArchiveItems = new List<ArchiveItem>();
        public ArchiveEntity()
        {

        }
        public void Load()
        {
            List<string> files = Directory.EnumerateFileSystemEntries(this.FullPath, "*.niz", SearchOption.AllDirectories).ToList();
            ArchiveItems.Clear();
            int i = 0;
            using (ProcessForm p = new ProcessForm(out ProgressChangedEventHandler a, out ProgressCompletedEventHandler b, out ProgressExceptionEventHandler c))
            {
                foreach (var file in files)
                {
                    i++;
                    try
                    {
                        var it = new ArchiveItem(file);
                        this.ArchiveItems.Add(it);
                        int pvalue = (int)((double)i / (double)files.Count * 100D);
                        a.Invoke(null, new ProgressChangedEventArgs(pvalue, it.Name));
                    }
                    catch (Exception e1)
                    {
                        c.Invoke(null, new ProgressExceptionEventArgs(e1, file));
                        MessageBox.Show($"加载文件失败：{e1.Message}\n\n在文件：{file}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
                b.Invoke(null, new ProgressCompletedEventArgs());
            }
            this.ArchiveItems.Sort();
        }
        public override string ToString()
        {
            return JsonSerializer.Serialize(this, AppSetting.SerializerOptions);
        }
        public int CompareTo(ArchiveEntity? other)
        {
            if (other == null) return -1;
            if (this.SortNumber > other.SortNumber)
            {
                return 1;
            }
            else if (this.SortNumber < other.SortNumber)
            {
                return -1;
            }
            else
                return 0;
        }
    }
    public class ArchiveItem : IComparable<ArchiveItem>
    {
        public string FullName;
        public string Name
        {
            get
            {
                return Path.GetFileName(FullName);
            }
        }
        private string m_Description = String.Empty;
        public string Description
        {
            get => m_Description;
            set
            {
                if (m_Description != value)
                {
                    if (File.Exists(this.FullName))
                    {
                        using (DefPackArchive stream = DefPackArchive.CreateWith(this.FullName))
                        {
                            stream.Note = value;
                            this.Length = stream.Length;
                        }
                    }
                }
                m_Description = value;
            }
        }
        private DateTime m_CreateTime;

        public DateTime CreateTime
        {
            get
            {
                return m_CreateTime;
            }
        }
        public long Length;
        public ArchiveItem(string fullName)
        {
            this.FullName = fullName;
            if (File.Exists(this.FullName))
            {
                using (DefPackArchive stream = DefPackArchive.CreateWith(this.FullName))
                {
                    this.Length = stream.Length;
                    this.m_Description = stream.Note.IsNullOrEmpty() ? "" : stream.Note!;
                }
                try
                {
                    int s = int.Parse(Path.GetFileNameWithoutExtension(Name));
                    m_CreateTime = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1), TimeZoneInfo.Local).AddSeconds(s);
                }
                catch
                {
                    m_CreateTime = File.GetLastWriteTime(this.FullName);
                }
            }
        }

        public int CompareTo(ArchiveItem? other)
        {
            if (other == null) return 0;
            if (this.CreateTime > other.CreateTime)
                return -1;
            else if (this.CreateTime < other.CreateTime)
                return 1;
            else
                return 0;
        }
        //public ArchiveItem(string fullName, string description = null)
        //{
        //    this.FullName = fullName;
        //    this.m_Description = description;
        //}
    }

    public class BackupEntity
    {
        public List<FileItem> SubFiles = new List<FileItem>();
        public List<FileItem> Polling(ArchiveEntity archive, bool isManual = false)
        {
            string[] notDetectArray = archive.UnDetect.Replace("*", "").Split(';');
            IEnumerable<string> files = Directory.EnumerateFileSystemEntries(archive.ArchivePath.ExpandEnvironmentVariables(), "*", SearchOption.AllDirectories);
            List<FileItem> newFiles = new List<FileItem>();
            bool isFind;
            foreach (var file in files)
            {
                var it = new FileItem(file, archive.TriggerEvent == 1);
                isFind = it.Name.HasString(notDetectArray);
                if (Directory.Exists(file))
                    isFind = true;
                if (!isFind)
                    newFiles.Add(it);
            }
            if (isManual)
            {
                SubFiles = newFiles;
                return newFiles;
            }
            if (SubFiles.Count == 0 && newFiles.Count > 0)
            {
                SubFiles = newFiles;
                return new List<FileItem>();
            }
            List<FileItem> result = new List<FileItem>();
            foreach (var item in newFiles)
            {
                isFind = false;
                foreach (var i in SubFiles)
                {
                    if (i.FullName == item.FullName)
                    {
                        if (archive.TriggerEvent == 0)
                        {
                            if (i.ModiflyTime != item.ModiflyTime)
                            {
                                result.Add(item);
                            }
                        }
                        else if (i.Crc32 != item.Crc32)
                        {
                            result.Add(item);
                        }
                        SubFiles.Remove(i);
                        isFind = true;
                        break;
                    }
                }
                if (!isFind)
                    result.Add(item);
            }
            SubFiles = newFiles;
            if (result.Count > 0 && archive.BackupContent == 0) result = newFiles;
            return result;
        }
        public static ArchiveItem Backup(ArchiveEntity archive, List<FileItem> items, string? description = null)
        {
            string tmp = Path.GetTempPath() + "ArchiveBackup\\" + Path.GetFileName(archive.ArchivePath.ExpandEnvironmentVariables());
            string[] notBuckupArray = archive.UnBuckup.Replace("*", "").Split(';');
            string fullName;
            List<string> files = new List<string>();
            foreach (var item in items)
            {
                if (!item.Name.HasString(notBuckupArray))
                {
                    fullName = item.FullName.Replace(archive.ArchivePath.ExpandEnvironmentVariables() + "\\", tmp + "\\");
                    Utils.CommonUtil.MakeSureDirectoryPathExists(fullName);
                    if (File.Exists(fullName)) File.Delete(fullName);
                    File.Copy(item.FullName, fullName);
                    files.Add(fullName);
                }
            }
            uint time = (uint)(DateTime.Now - TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1), TimeZoneInfo.Local)).TotalSeconds;
            fullName = archive.FullPath + "\\" + time.ToString() + ".niz";
            DefPackArchive.CreateArchive(fullName, string.IsNullOrEmpty(description) ? null : description);
            using (DefPackArchive stream = DefPackArchive.CreateWith(fullName))
            {
                //IEnumerable<string> cmpFiles = Directory.EnumerateFileSystemEntries(tmp, "*", SearchOption.AllDirectories);
                //List<string> fullNames = cmpFiles.ToList();
                //FileInfo info = new FileInfo(tmp);
                //string path = tmp, cmpName;
                //if (!path.EndsWith("\\"))
                //    path += "\\";
                //Entry subfile;
                //foreach (string cmpFile in fullNames)
                //{
                //    cmpName = cmpFile.Replace(path, "");
                //    subfile = stream.CreateEntry(cmpFile, cmpName, CompressionMethod.Optimal);
                //    stream.CompressFile(subfile, cmpFile);
                //}
                stream.CompressDirectory(tmp);
            }
            DirectoryInfo di = new DirectoryInfo(tmp);
            di.Delete(true);
            ArchiveItem ar = new ArchiveItem(fullName);
            return ar;
        }

    }
    public class FileItem
    {
        public string FullName;
        public string Name => Path.GetFileName(FullName);
        public DateTime ModiflyTime;
        public uint Crc32;
        public FileItem(string fullName, bool hashCheck = false)
        {
            FullName = fullName;
            ModiflyTime = File.GetLastWriteTime(fullName);
            if (hashCheck)
            {
                Crc32 = ComputeCrc32(fullName);
            }
        }
        private static uint ComputeCrc32(string fileName)
        {
            if (!File.Exists(fileName)) return 0;
            using (Crc32 crc = new Crc32())
            {
                using (FileStream file = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite))
                {
                    using BinaryReader br = new BinaryReader(file);
                    while (br.BaseStream.Position < br.BaseStream.Length)
                    {
                        crc.Compute(br.ReadBytes(4194304));
                    }
                    br.Close();
                }
                return crc.Finish();
            }
        }
    }
    public class AppSetting
    {
        public static JsonSerializerOptions SerializerOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, NumberHandling = JsonNumberHandling.AllowReadingFromString, Encoder = JavaScriptEncoder.Create(UnicodeRanges.All) };
        public string? LastContent { get; set; }
        public string DefPackExe { get; set; } = ".\\DefPack.UI.exe";
        public string BackupPath { get; set; } = Path.Combine(Application.StartupPath, "backup");
        public List<GroupEntity> Groups { get; set; } = new List<GroupEntity>();
        public List<ArchiveEntity> Items { get; set; } = new List<ArchiveEntity>();
        public AppSetting()
        {
            //GroupEntity groupDefault = new GroupEntity() { Name = "最近访问" };
            //GroupEntity groupAll = new GroupEntity() { Name = "所有项目" };
            //Groups.Add(groupDefault);
            //Groups.Add(groupAll);
        }
        public override string ToString()
        {
            return JsonSerializer.Serialize(this, AppSetting.SerializerOptions);
        }
        public void Save(string filename)
        {
            using (FileStream stream = File.Open(filename, FileMode.OpenOrCreate))
            {
                stream.SetLength(0);
                using (BinaryWriter writer = new BinaryWriter(stream))
                {
                    writer.Write(Encoding.UTF8.GetBytes(this.ToString()));
                }
            }
        }
        public static string ReadFile(string filename)
        {
            if (!File.Exists(filename)) throw new FileNotFoundException($"无法加载文件，文件不存在：\n{filename}");
            using StreamReader sr = File.OpenText(filename);
            string cache = sr.ReadToEnd();
            sr.Close();
            return cache;
        }
    }
    public class GroupEntity : IComparable<GroupEntity>
    {
        [NonSerialized]
        public int Index;
        public int SortNumber { get; set; } = 99;
        public string Name { get; set; }
        public string UUID { get; set; } = Guid.NewGuid().ToString().ToUpper().Replace("-", "");

        public int CompareTo(GroupEntity? other)
        {
            if (other == null) return -1;
            if (this.SortNumber > (other.SortNumber))
            {
                return 1;
            }
            else if (this.SortNumber < other.SortNumber)
            {
                return -1;
            }
            else
                return 0;
        }
    }
}
